#include "../apue.h"
#include "deal_func.h"
#define MY_SIGNAL (SIGRTMIN + 2)

struct rec_to_manage udp_rcv()
{
	int sockfd;
	struct ftp_info rec_cli_cmd;
	struct rec_to_manage rec_info_to_manage;
	struct sockaddr_in conaddr, selfaddr;
	int conlen = sizeof(conaddr);
	int n;

	bzero(&rec_info_to_manage, sizeof(rec_info_to_manage));
	bzero(&rec_cli_cmd, sizeof(rec_cli_cmd));
	bzero(&conaddr, sizeof(conaddr));

	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sockfd < 0) {
		perror("socket");
		exit(1);
	}

	bzero(&selfaddr, sizeof(selfaddr));
	selfaddr.sin_family = AF_INET;
	selfaddr.sin_port = htons(6111);
	selfaddr.sin_addr.s_addr = inet_addr(INADDR_ANY);
	bind(sockfd, (struct sockaddr *)&selfaddr, sizeof(selfaddr));

	while (1) {
		n = recvfrom(sockfd, &rec_cli_cmd, sizeof(rec_cli_cmd),
			     0, (struct sockaddr *)&conaddr, &conlen);
	}

	rec_info_to_manage.info = rec_cli_cmd;
	rec_info_to_manage.addr = conaddr;
	return rec_info_to_manage;

}

int judge(struct rec_to_manage rec_info_to_manage,
	  struct login_data *shm_memory)
{
	int i = 0;
	int flag = 0;
	while (i < 50) {
		if (shm_memory[i].rec_info.addr.sin_addr.s_addr ==
		    rec_info_to_manage.addr.sin_addr.s_addr) {
			if (shm_memory[i].rec_info.info.cli_pid ==
			    rec_info_to_manage.info.cli_pid) {
				flag = 1;
				break;
			}
		}
		i++;
	}
	return flag;
}

void init_shm_memory(struct login_data *shm_memory)
{
	int shmid;
	int ret;
	int i;
	int flag = 0;
	sem_t *psem = NULL;
	struct login_data *ptmp = NULL;
	shmid = shm_open("/shm", O_CREAT | O_EXCL | O_RDWR, 0777);
	if (shmid < 0) {
		perror("shm_open");
	}
	ftruncate(shmid, sizeof(shm_memory));
	/*ptmp map with shmid */
	ptmp = mmap(NULL, sizeof(shm_memory),
		    PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
	psem = sem_open("/sem", O_CREAT | O_RDWR, 0777, 1);
	if (psem == SEM_FAILED) {
		perror("sem_open");
	}
	for (i = 0; i < 50; i++) {
		bzero(&shm_memory[i], sizeof(shm_memory[i]));
	}
	//sem_wait(psem);
	//      sem_post(psem);
	return;
}

int insert_to_memory(struct login_data *shm_memory,
		     struct rec_to_manage rec_info_to_manage)
{
	int i;
	for (i = 0; i < 50; i++) {
		if (shm_memory[i].rec_info.addr.sin_addr.s_addr == 0) {
			/*connect ip */
			shm_memory[i].rec_info = rec_info_to_manage;
			break;
		}
	}
	return i;
}

int main()
{
	struct rec_to_manage rec_info_to_manage;
	int pid_1, pid_2;
	struct login_data shm_memory[50];
	//struct manage_to_execute execute_info;
	int pipe_fid[2];

	/*create pipe */
	pipe(pipe_fid);

	/*receive cmd form client, return cmd information */
	bzero(&rec_info_to_manage, sizeof(rec_info_to_manage));
	/*init shared memory */
	init_shm_memory(shm_memory);
	/*
	   first receive is login information, 
	   judge the login info is right or not
	 */
	//rec_info_to_manage = udp_rcv();
	//judge_login(rec_info_to_manage);
	pid_1 = fork();
	if (pid_1 < 0) {
		perror("fork");
		exit(0);
	}
	/*receive process receive infomation from client ,
	   it received a structure: cmd, pathname, cli_pid
	 */
	if (pid_1 == 0) {
		close(pipe_fid[0]);
		while (1) {
			bzero(&rec_info_to_manage, sizeof(rec_info_to_manage));
			rec_info_to_manage = udp_rcv();
			write(pipe_fid[1], &rec_info_to_manage, sizeof(rec_info_to_manage));
		}
		close(pipe_fid[1]);
	}
	/*
	   manage process judge this user is already login or not,
	   if login: call the child process derectly
	   if not login: create a child process,create a new fifo named on child pid.
	 */
	else {
		/* deal process shm_open(),manage client information */
		/*deal_process endless loop */
		while (1) {
			int n, i;
			int flag;
			n = read(pipe_fid[0], &rec_info_to_manage,
				 sizeof(rec_info_to_manage));
			flag = judge(rec_info_to_manage, shm_memory);

			if (flag == 1) {
				int fifo_id;
				pid_t pid;

				/*when this info is in memory,callback the process you already create */
				for (i = 0; i < 50; i++) {
					if (rec_info_to_manage.addr.sin_addr.s_addr == shm_memory[i].rec_info.addr.sin_addr.s_addr) {
						if (rec_info_to_manage.info.cli_pid == shm_memory[i].rec_info.info.cli_pid) {
							fifo_id = open(shm_memory[i].pid_fifo, O_WRONLY);
							write(fifo_id, &rec_info_to_manage, sizeof(rec_info_to_manage));
							pid = atoi(shm_memory[i].pid_fifo);
							kill(pid, MY_SIGNAL);

						}
					}
				}
			} else {
				/*ret_num is for login_data.pid_fifo[20] */
				int ret_num;
				/*when this info is not in memory,insert this infomation to memory */
				ret_num = insert_to_memory(shm_memory, rec_info_to_manage);
				pid_2 = fork();
				if (pid_2 < 0) {
					perror("fork");
				}
				if (pid_2 == 0) {
					/*create fifo to communicate with pid_2 */
					int fifo;
					int fifo_id;

					sprintf(shm_memory[ret_num].pid_fifo, "%d", getpid());
					/*create a fifo named this chil_process pid */
					mkfifo(shm_memory[ret_num].pid_fifo, 0777);
					fifo_id = open(shm_memory[ret_num].pid_fifo, O_RDONLY);
					if (fifo_id < 0) {
						perror("open");
						exit(1);
					}
					while (1) {
						read(fifo_id, &rec_info_to_manage, sizeof(rec_info_to_manage));
						deal_func(rec_info_to_manage, shm_memory);
					}
				}
			}
		}

	}

	shm_unlink("/shm");
	sem_unlink("/sem");

}
